﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using iTool.Cloud.Database.iToolService;
using iTool.Cloud.Database.KeyGeneratorProvider.Contract;
using iTool.Cloud.Database.Options;

using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Options;

using Orleans.Concurrency;
using Orleans.Configuration;
using Orleans.Runtime;

using static Lucene.Net.Util.Fst.Util;

namespace iTool.Cloud.Database.ServiceProvider.Implementation
{
    /// <summary>
    /// 以Table 为主题
    /// </summary>
    [StatelessWorker]
    public class TableReaderService : iToolServiceDataBase, iTableReaderService
    {
        public TableReaderService(KeyGeneratorOptions options, IOptions<ClusterOptions> clusterOptions, ISiloStatusOracle siloStatusOracle) : base(options, clusterOptions,siloStatusOracle)
        {

        }

        public async Task<(string data, int total, string token)> ExecuteReaderAsync(AnalysisExecuteItemOptions query)
        {
            if (query.Action != SQL.AIHandle.SQLActionOptions.SELECT)
            {
                throw new Exception("No query please use ExecuteNonQueryAsync");
            }

            var result = await GetFormatSqlAsync(query);

            await using (var connection = await GetConnectionAsync(query.CustomFunctions, true))
            {
                await using (var command = new SqliteCommand(query.QuerySql, connection))
                {
                    if (query.Parameters.Any()) command.Parameters.AddRange(query.Parameters);

                    await using (var reader = await command.ExecuteReaderAsync())
                    {
                        var json = ToJson(reader);

                        if (result.Any())
                        {
                            var last = result.Where(item => item.LastScoreDocItem != null).FirstOrDefault();
                            if (last.LastScoreDocItem != null || last.TotalHits > 0)
                            {
                                return (json, last.TotalHits, last.LastScoreDocItem.ToString());
                            }
                            else
                            {
                                last = result.Last();
                                return (json, last.TotalHits, last.LastScoreDocItem?.ToString() ?? string.Empty);
                            }
                        }

                        return (json, 0, string.Empty);
                    }
                }
            }
        }

        public async Task<object> ExecuteScalarAsync(AnalysisExecuteItemOptions query)
        {
            if (query.Action != SQL.AIHandle.SQLActionOptions.SELECT)
            {
                throw new Exception("No query please use ExecuteNonQueryAsync");
            }

            await GetFormatSqlAsync(query);

            await using (var connection = await GetConnectionAsync(query.CustomFunctions, true))
            {
                await using (var command = new SqliteCommand(query.QuerySql, connection))
                {
                    if (query.Parameters.Any()) command.Parameters.AddRange(query.Parameters);
                    return await command.ExecuteScalarAsync();
                }
            }
        }

        public async override Task OnActivateAsync()
        {
            await base.OnActivateAsync();
        }

        private string ToJson(DbDataReader dataReader)
        {
            Dictionary<int, (Type, string)> keyValues = new Dictionary<int, (Type, string)>();
            for (int i = 0; i < dataReader.FieldCount; i++)
            {
                var field = dataReader.GetName(i);
                Type type = dataReader.GetFieldType(i);
                keyValues.Add(i, (type, field));
            }

            StringBuilder jsonString = new StringBuilder("[");
            while (dataReader.Read())
            {
                jsonString.Append('{');

                for (int i = 0; i < dataReader.FieldCount; i++)
                {
                    jsonString.Append(string.Format("\"{0}\":", keyValues[i].Item2));
                    if (keyValues[i].Item1 == typeof(string))
                        jsonString.Append(string.Format("\"{0}\",", dataReader[i]));
                    else
                        jsonString.Append(string.Format("{0},", dataReader[i]));
                }

                jsonString[^1] = '}';
                jsonString.Append(",");
            }

            if (dataReader.HasRows)
                jsonString[^1] = ']';
            else
                jsonString.Append(']');

            _ = keyValues;
            return jsonString.ToString();
        }
    }

    [StatelessWorker]
    public class TableReaderService<TReader> : iToolServiceDataBase, iTableReaderService<TReader>
        where TReader : class, new()
    {
        public TableReaderService(KeyGeneratorOptions options, IOptions<ClusterOptions> clusterOptions, ISiloStatusOracle siloStatusOracle) : base(options, clusterOptions, siloStatusOracle)
        {

        }

        public async override Task OnActivateAsync()
        {
            await base.OnActivateAsync();
        }

        public async Task<(List<TReader> data, int total, string token)> ExecuteReaderAsync(AnalysisExecuteItemOptions query)
        {
            if (query.Action != SQL.AIHandle.SQLActionOptions.SELECT)
            {
                throw new Exception("No query please use ExecuteNonQueryAsync");
            }

            var result = await GetFormatSqlAsync(query);

            await using (var connection = await GetConnectionAsync(query.CustomFunctions, true))
            {
                await using (var command = new SqliteCommand(query.QuerySql, connection))
                {
                    if (query.Parameters.Any()) command.Parameters.AddRange(query.Parameters);
                    var reader = await command.ExecuteReaderAsync();
                    var data = PropertyUtils<TReader>.ConvertDataReaderToEntity(reader);

                    if (result.Any())
                    {
                        var last = result.Where(item => item.LastScoreDocItem != null).FirstOrDefault();
                        if (last.LastScoreDocItem != null || last.TotalHits > 0)
                        {
                            return (data, last.TotalHits, last.LastScoreDocItem?.ToString() ?? string.Empty);
                        }
                        else
                        {
                            last = result.Last();
                            return (data, last.TotalHits, last.LastScoreDocItem?.ToString() ?? string.Empty);
                        }
                    }

                    return (data, 0, string.Empty);
                }
            }
        }
    }
}
